#include "wlistsourcemodel.h"
#include "wlistpatient.h"
#include "dcmtk/dcmdata/dctk.h"
#include "privatetag.h"
#include <QDir>
#include <QtConcurrent>
#include <QFutureWatcher>

#include "../MainStation/mainwindow.h"
#include "../share/global.h"

static QDate dateSetup(const QString &dateStr);
static QTime timeSetup(const QString &timeStr);
static WListPatient *readPatientFile(const QFileInfo &file);

WListSourceModel::WListSourceModel(QObject *parent) :
    QAbstractItemModel(parent)
{
    connect(&watcher, SIGNAL(resultReadyAt(int)), this, SLOT(onResultReadyAt(int)));
    connect(&watcher, SIGNAL(finished()), this, SLOT(onWatcherFinished()));
    setupModel();
}

WListSourceModel::~WListSourceModel()
{
    qDeleteAll(patientList);
    patientList.clear();
}

WListPatient* readPatientFile(const QFileInfo &file)
{
    DcmFileFormat dcmfile;
    DcmItem *dset = dcmfile.getDataset();
    WListPatient *patient = 0;
    const char *value = 0;
     if (EC_Normal == dcmfile.loadFile(file.filePath().toLocal8Bit())) {
        patient = new WListPatient;
        patient->fileName = file.filePath();

        dset->findAndGetString(DCM_SOPInstanceUID, value);
        patient->instanceUID = QString::fromLatin1(value);
        dset->findAndGetString(DCM_InstanceCreationDate, value);
        patient->creationTime.setDate(dateSetup(QString::fromLatin1(value)));
        dset->findAndGetString(DCM_InstanceCreationTime, value);
        patient->creationTime.setTime(timeSetup(QString::fromLatin1(value)));
        dset->findAndGetString(DCM_InstanceLastModifiedDate, value);
        patient->lastModifiedTime.setDate(dateSetup(QString::fromLatin1(value)));
        dset->findAndGetString(DCM_InstanceLastModifiedTime, value);
        patient->lastModifiedTime.setTime(timeSetup(QString::fromLatin1(value)));

        dset->findAndGetString(DCM_AdmissionID, value);
        patient->admissionID = QString::fromLatin1(value);
        dset->findAndGetString(DCM_InstitutionalDepartmentName, value);
        patient->department = QString::fromLatin1(value);

        dset->findAndGetString(DCM_AccessionNumber, value);
        patient->accNumber = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_StudyInstanceUID, value);
        patient->studyUID = QString::fromLatin1(value);
        dset->findAndGetString(DCM_PatientID, value);
        patient->patientID = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_PatientName, value);
        patient->patientName = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_PatientSex, value);
        patient->patientSex = QString::fromLocal8Bit(value).remove(' ');
        dset->findAndGetString(DCM_PatientBirthDate, value);
        patient->patientBirth = dateSetup(QString::fromLocal8Bit(value));
        dset->findAndGetString(DCM_PatientAge, value);
        patient->patientAge = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_MedicalAlerts, value);
        patient->medicalAlerts = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_PatientSize, value);
        patient->patientSize = QString::fromLatin1(value).toDouble();
        dset->findAndGetString(DCM_PatientWeight, value);
        patient->patientWeight = QString::fromLatin1(value).toInt();
        dset->findAndGetString(DCM_PatientTelephoneNumbers, value);
        patient->patientPhone = QString::fromLatin1(value);
        dset->findAndGetString(DCM_PatientAddress, value);
        patient->patientAddress = QString::fromLocal8Bit(value);

        dset->findAndGetString(DCM_RequestingPhysician, value);
        patient->reqPhysician = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_RequestedProcedureID, value);
        patient->reqProcID = QString::fromLocal8Bit(value);
        dset->findAndGetString(DCM_RequestedProcedurePriority, value);
        patient->reqProcPrio = QString::fromLocal8Bit(value).remove(' ');
        dset->findAndGetString(DCM_RequestedProcedureDescription, value);
        patient->reqProcDesc = QString::fromLocal8Bit(value);

        dset->findAndGetSequenceItem(DCM_ScheduledProcedureStepSequence, dset);
        if (dset) {
            dset->findAndGetString(DCM_Modality, value);
            patient->modality = QString::fromLocal8Bit(value);
            dset->findAndGetString(DCM_ScheduledPerformingPhysicianName, value);
            patient->schPhysician = QString::fromLocal8Bit(value);
            dset->findAndGetString(DCM_ScheduledStationAETitle, value);
            patient->schStationAET = QString::fromLocal8Bit(value);
            dset->findAndGetString(DCM_ScheduledStationName, value);
            patient->schStationName = QString::fromLocal8Bit(value);
            dset->findAndGetString(DCM_ScheduledProcedureStepStartDate, value);
            patient->schDateTime.setDate(dateSetup(QString::fromLatin1(value)));
            dset->findAndGetString(DCM_ScheduledProcedureStepStartTime, value);
            patient->schDateTime.setTime(timeSetup(QString::fromLatin1(value)));
            dset->findAndGetString(DCM_ScheduledProcedureStepID, value);
            patient->schProcStepID = QString::fromLocal8Bit(value);
            dset->findAndGetString(DCM_ScheduledProcedureStepDescription, value);
            patient->schProcStepDesc = QString::fromLocal8Bit(value);
        }
    }

     return patient;
}

void WListSourceModel::onResultReadyAt(int index)
{
    WListPatient *patient = watcher.resultAt(index);
    if (patient) insertPatient(patient, patientList.size());
}

void WListSourceModel::setupModel()
{
    beginResetModel();
    qDeleteAll(patientList);
    patientList.clear();
    endResetModel();

    QString dbpath = QString("%1/%2").arg(mainWindow->getDbConfig().imagePath,
                                          mainWindow->getStationInfo().aetitle);
    QFileInfoList files = QDir(dbpath).entryInfoList(QDir::Files);

    watcher.setFuture(QtConcurrent::mapped(files, readPatientFile));
}

QDate dateSetup(const QString &dateStr)
{
    QDate date;
    if (dateStr.size() >= 8) {
        int year = dateStr.left(4).toInt();
        int month = dateStr.mid(4, 2).toInt();
        int day = dateStr.mid(6, 2).toInt();
        date.setDate(year, month, day);
    }
    return date;
}

QTime timeSetup(const QString &timeStr)
{
    QTime time;
    if (timeStr.size() >= 6) {
        int hour = timeStr.left(2).toInt();
        int minute = timeStr.mid(2, 2).toInt();
        int second = timeStr.mid(4, 2).toInt();
        time.setHMS(hour, minute, second);
    }
    return time;
}

int WListSourceModel::insertPatient(WListPatient *patient, int row, const QModelIndex &parent)
{
    int result = 0;
    if (!patient && (row < 0 || row > patientList.size()) && parent.isValid()) return result;

    result = saveFileFormatToDB(*patient);
    if (result) {
        beginInsertRows(parent, row, row);
        patientList.insert(row, patient);
        endInsertRows();
    }
    return result;
}

int WListSourceModel::saveFileFormatToDB(WListPatient &patient)
{
    DcmDataset *dset = new DcmDataset;
    OFCondition result;

    result = dset->putAndInsertString(DCM_SpecificCharacterSet, "GB18030");
    dset->putAndInsertString(DCM_InstanceCreationDate, patient.creationTime.date().toString("yyyyMMdd").toLatin1());
    dset->putAndInsertString(DCM_InstanceCreationTime, patient.creationTime.time().toString("hhmmss").toLatin1());
    result = dset->putAndInsertString(DCM_SOPClassUID, UID_GeneralPurposeScheduledProcedureStepSOPClass);
    result = dset->putAndInsertString(DCM_SOPInstanceUID, patient.instanceUID.toLatin1());
    result = dset->putAndInsertString(DCM_AccessionNumber, patient.accNumber.toLocal8Bit());

    result = dset->putAndInsertString(DCM_InstanceLastModifiedDate, patient.lastModifiedTime.date().toString("yyyyMMdd").toLatin1());
    result = dset->putAndInsertString(DCM_InstanceLastModifiedTime, patient.lastModifiedTime.time().toString("hhmmss").toLatin1());
    if (!patient.admissionID.isEmpty())
        result = dset->putAndInsertString(DCM_AdmissionID, patient.admissionID.toLatin1());
    if (!patient.department.isEmpty())
        result = dset->putAndInsertString(DCM_InstitutionalDepartmentName, patient.department.toLatin1());

    result = dset->putAndInsertString(DCM_PatientID, patient.patientID.toLocal8Bit());
    result = dset->putAndInsertString(DCM_PatientName, patient.patientName.toLocal8Bit());
    result = dset->putAndInsertString(DCM_PatientSex, patient.patientSex.toLocal8Bit());
    result = dset->putAndInsertString(DCM_PatientBirthDate, patient.patientBirth.toString("yyyyMMdd").toLatin1());
    result = dset->putAndInsertString(DCM_PatientAge, patient.patientAge.toLocal8Bit().data());
    result = dset->putAndInsertString(DCM_MedicalAlerts, patient.medicalAlerts.toLocal8Bit().data());
    if (patient.patientSize > 0)
        result = dset->putAndInsertString(DCM_PatientSize, QString::number(patient.patientSize).toLatin1());
    if (patient.patientWeight > 0)
        result = dset->putAndInsertString(DCM_PatientWeight, QString::number(patient.patientWeight).toLatin1());
    if (!patient.patientPhone.isEmpty())
        result = dset->putAndInsertString(DCM_PatientTelephoneNumbers, patient.patientPhone.toLatin1());
    if (!patient.patientAddress.isEmpty())
        result = dset->putAndInsertString(DCM_PatientAddress, patient.patientAddress.toLocal8Bit());

    result = dset->putAndInsertString(DCM_StudyInstanceUID, patient.studyUID.toLatin1());
    if (patient.reqPhysician.isEmpty()) patient.reqPhysician = " ";
    result = dset->putAndInsertString(DCM_RequestingPhysician, patient.reqPhysician.toLocal8Bit());
    if (patient.reqProcID.isEmpty()) patient.reqProcID = " ";
    result = dset->putAndInsertString(DCM_RequestedProcedureID, patient.reqProcID.toLocal8Bit());
    if (patient.reqProcDesc.isEmpty()) patient.reqProcDesc = " ";
    result = dset->putAndInsertString(DCM_RequestedProcedureDescription, patient.reqProcDesc.toLocal8Bit());
    result = dset->putAndInsertString(DCM_RequestedProcedurePriority, patient.reqProcPrio.toLocal8Bit());

    DcmItem *item = new DcmItem;
    result = item->putAndInsertString(DCM_Modality, patient.modality.toLocal8Bit());
    if (patient.schPhysician.isEmpty()) patient.schPhysician = " ";
    result = item->putAndInsertString(DCM_ScheduledPerformingPhysicianName, patient.schPhysician.toLocal8Bit());
    if (patient.schStationAET.isEmpty()) patient.schStationAET = " ";
    result = item->putAndInsertString(DCM_ScheduledStationAETitle, patient.schStationAET.toLocal8Bit());
    if (patient.schStationName.isEmpty()) patient.schStationName = " ";
    result = item->putAndInsertString(DCM_ScheduledStationName, patient.schStationName.toLocal8Bit());
    if (patient.schProcStepID.isEmpty()) patient.schProcStepID = " ";
    result = item->putAndInsertString(DCM_ScheduledProcedureStepID, patient.schProcStepID.toLocal8Bit());
    if (patient.schProcStepDesc.isEmpty()) patient.schProcStepDesc = " ";
    result = item->putAndInsertString(DCM_ScheduledProcedureStepDescription, patient.schProcStepDesc.toLocal8Bit());
    result = item->putAndInsertString(DCM_ScheduledProcedureStepStartDate, patient.schDateTime.date().toString("yyyyMMdd").toLatin1());
    result = item->putAndInsertString(DCM_ScheduledProcedureStepStartTime, patient.schDateTime.time().toString("hhmmss").toLatin1());
    dset->insertSequenceItem(DCM_ScheduledProcedureStepSequence, item);

    result = dset->saveFile(patient.fileName.toLocal8Bit(), EXS_LittleEndianExplicit);
    delete dset;
    return result.good();
}

int WListSourceModel::columnCount(const QModelIndex &parent) const
{
    return WListPatient::ColumnCount;
}

int WListSourceModel::rowCount(const QModelIndex &parent) const
{
    return patientList.size();
}
/*
bool WListSourceModel::insertRows(int row, int count, const QModelIndex &parent)
{
    if (!parent.isValid()) {
        beginInsertRows(parent, row, row+count-1);
        for (int i = 0; i < count; ++i)
            patientList.insert(row+i, new WListPatient);
        endInsertRows();
        return true;
    }
    return false;
}
*/
bool WListSourceModel::removeRows(int row, int count, const QModelIndex &parent)
{
    if (!parent.isValid()) {
        beginRemoveRows(parent, row, row+count-1);
        for (int i = 0; i < count; ++i) {
            WListPatient *patient = patientList.takeAt(row);
            unlink(patient->fileName.toLocal8Bit());
            delete patient;
        }
        endRemoveRows();
        return true;
    }
    return false;
}

QModelIndex WListSourceModel::parent(const QModelIndex &child) const
{
    return QModelIndex();
}

QModelIndex WListSourceModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!parent.isValid() && row >= 0 && row < patientList.size())
        return createIndex(row, column, patientList.at(row));
    return QModelIndex();
}

QVariant WListSourceModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole && index.isValid()) {
        WListPatient *patient = static_cast<WListPatient*>(index.internalPointer());
        switch ((WListPatient::ColumnType)index.column()) {
        case WListPatient::AccNumber:
            return QVariant(patient->accNumber);
        case WListPatient::PatientID:
            return QVariant(patient->patientID);
        case WListPatient::PatientName:
            return QVariant(patient->patientName);
        case WListPatient::PatientSex:
            return sex2trSex(patient->patientSex);
        case WListPatient::PatientBirth:
            return QVariant(patient->patientBirth);
        case WListPatient::PatientAge: {
            QString ageStr = patient->patientAge.remove(" ");
            QString ageUnit = ageStr.right(1);
            if ((ageUnit=="Y") || (ageUnit=="y")) {
                return QString("%1%2").arg(ageStr.left(ageStr.size()-1), tr("Years"));
            } else if ((ageUnit=="M") || (ageUnit=="m")) {
                return QString("%1%2").arg(ageStr.left(ageStr.size()-1), tr("Months"));
            } else if ((ageUnit=="W") || (ageUnit=="w")) {
                return QString("%1%2").arg(ageStr.left(ageStr.size()-1), tr("Weeks"));
            } else if ((ageUnit=="D") || (ageUnit=="d")) {
                return QString("%1%2").arg(ageStr.left(ageStr.size()-1), tr("Days"));
            } else {
                return ageStr;
            }
        }
        case WListPatient::MedicalAlerts:
            return patient->medicalAlerts;

        case WListPatient::ReqPhysician:
            return QVariant(patient->reqPhysician);
        case WListPatient::ReqProcID:
            return QVariant(patient->reqProcID);
        case WListPatient::ReqProcPrio:
            if (patient->reqProcPrio == "LOW") return QVariant(tr("LOW"));
            else if (patient->reqProcPrio == "MEDIUM") return QVariant(tr("MEDIUM"));
            else return QVariant(tr("HIGH"));
        case WListPatient::ReqProcDesc:
            return QVariant(patient->reqProcDesc);

        case WListPatient::Modality:
            return QVariant(patient->modality);
        case WListPatient::SchPhysician:
            return QVariant(patient->schPhysician);
        case WListPatient::SchStationAE:
            return QVariant(patient->schStationAET);
        case WListPatient::SchStationName:
            return QVariant(patient->schStationName);
        case WListPatient::SchDateTime:
            return QVariant(patient->schDateTime);
        case WListPatient::SchProcStepID:
            return QVariant(patient->schProcStepID);
        case WListPatient::SchProcStepDesc:
            return QVariant(patient->schProcStepDesc);

        case WListPatient::AdmissionID:
            return QVariant(patient->admissionID);
        case WListPatient::Department:
            return QVariant(patient->department);
        case WListPatient::PatientSize:
            return QVariant(patient->patientSize);
        case WListPatient::PatientWeight:
            return QVariant(patient->patientWeight);
        case WListPatient::PatientPhone:
            return QVariant(patient->patientPhone);
        case WListPatient::PatientAddress:
            return QVariant(patient->patientAddress);
        case WListPatient::CreationDateTime:
            return QVariant(patient->creationTime);
        case WListPatient::LastModifiedDateTime:
            return QVariant(patient->lastModifiedTime);
        default:
            return QVariant();
        }
    }
    return QVariant();
}

bool WListSourceModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid()) return false;
    if (saveFileFormatToDB(*(static_cast<WListPatient*>(index.internalPointer()))))
        emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
                        createIndex(index.row(), WListPatient::ColumnCount-1, index.internalPointer()),
                        QVector<int>()<<role);
    return true;
}

QVariant WListSourceModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (Qt::Horizontal == orientation) {
        if (Qt::DisplayRole == role) {
            switch (section) {
            case WListPatient::AccNumber:
                return tr("Acc Number");
            case WListPatient::PatientID:
                return tr("Patient ID");
            case WListPatient::PatientName:
                return tr("Patient Name");
            case WListPatient::PatientSex:
                return tr("Sex");
            case WListPatient::PatientBirth:
                return tr("Birth Date");
            case WListPatient::PatientAge:
                return tr("Age");

            case WListPatient::ReqPhysician:
                return tr("Req Physician");
            case WListPatient::ReqProcID:
                return tr("Req Proc ID");
            case WListPatient::ReqProcPrio:
                return tr("Req Priority");
            case WListPatient::ReqProcDesc:
                return tr("Req Proc Desc");

            case WListPatient::Modality:
                return tr("Modality");
            case WListPatient::SchStationAE:
                return tr("Sch Station AE");
            case WListPatient::SchStationName:
                return tr("Sch Station Name");
            case WListPatient::SchPhysician:
                return tr("Sch Physician");
            case WListPatient::SchDateTime:
                return tr("Sch DateTime");
            case WListPatient::SchProcStepID:
                return tr("Sch Proc Step ID");
            case WListPatient::SchProcStepDesc:
                return tr("Sch Proc Step Desc");

            case WListPatient::AdmissionID:
                return tr("Admission ID");
            case WListPatient::Department:
                return tr("Department");
            case WListPatient::MedicalAlerts:
                return tr("Medical Alerts");
            case WListPatient::PatientSize:
                return tr("Size(cm)");
            case WListPatient::PatientWeight:
                return tr("Weight(Kg)");
            case WListPatient::PatientPhone:
                return tr("Phone Number");
            case WListPatient::PatientAddress:
                return tr("Address");
            case WListPatient::CreationDateTime:
                return tr("Creation Time");
            case WListPatient::LastModifiedDateTime:
                return tr("Last Modified Time");

            default:
                return QVariant();
            }
        }
        return QVariant();
    } else {
        if (Qt::DisplayRole == role) {
            return QVariant(section+1);
        } else return QVariant();
    }
}
